package com.tank.components;

import com.almasb.fxgl.core.util.LazyValue;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.dsl.components.EffectComponent;
import com.almasb.fxgl.dsl.components.HealthIntComponent;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.EntityGroup;
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.time.LocalTimer;
import com.tank.TankApp;
import com.tank.constant.Config;
import com.tank.constant.Direction;
import com.tank.constant.GameType;
import com.tank.effect.FreezeEffect;
import com.tank.effect.ShipEffect;
import javafx.beans.value.ChangeListener;

import java.util.List;

public class TankComponent extends Component {

    private boolean moving = false;

    private double moveDistance = 0;

    private Direction direction = Direction.UP;

    private final LazyValue<EntityGroup> blockAllLazyValue = new LazyValue<>(() -> FXGL.getGameWorld().getGroup(GameType.BORDER, GameType.BRICK, GameType.STONE, GameType.SEA, GameType.PLAYER, GameType.ENEMY));

    private final LazyValue<EntityGroup> blockWithoutSeaLazyValue = new LazyValue<>(() -> FXGL.getGameWorld().getGroup(GameType.BORDER, GameType.BRICK, GameType.STONE, GameType.PLAYER, GameType.ENEMY));

    private LocalTimer shootTimer;

    private ChangeListener<Number> healChangeListener;

    public Direction getDirection() {
        return direction;
    }

    @Override
    public void onAdded() {
        shootTimer = FXGL.newLocalTimer();
        healChangeListener = (observable, oldValue, newValue) -> {
            if (newValue.intValue() <= 0) {
                GameType tankType = (GameType) entity.getType();
                if (tankType == GameType.PLAYER) {
                    FXGL.spawn("doomExplosion", entity.getCenter().subtract(50 / 2.0, 50/ 2.0));
                    FXGL.<TankApp>getAppCast().fail();
                } else {
                    FXGL.spawn("explosion", entity.getCenter().subtract(50 / 2.0, 50 / 2.0));
                }
                entity.removeFromWorld();
            }
        };
        entity.getComponent(HealthIntComponent.class).valueProperty().addListener(healChangeListener);
    }

    @Override
    public void onRemoved() {
    }

    @Override
    public void onUpdate(double tpf) {
        moveDistance = tpf * Config.PLAYER_SPEED;
        moving = false;
    }

    public void moveForDirection(Direction dir) {
        if (moving) {
            return;
        }
        if (entity.getComponent(EffectComponent.class).hasEffect(FreezeEffect.class)) {
            return;
        }
        moving = true;
        direction = dir;
        entity.setRotation(direction.getRotation());
        move();
    }

    private void move() {
        int len = (int) moveDistance;
        boolean shipEffect = entity.getComponent(EffectComponent.class).hasEffect(ShipEffect.class);
        List<Entity> blockers;
        if (shipEffect) {
            blockers = blockWithoutSeaLazyValue.get().getEntitiesCopy();
        } else {
            blockers = blockAllLazyValue.get().getEntitiesCopy();
        }
        blockers.remove(entity);
        outer:
        for (int i = 0; i < len; ++i) {
            entity.translate(direction.getVector().getX(), direction.getVector().getY());
            for (Entity blocker : blockers) {
                if (entity.isColliding(blocker)) {
                    entity.translate(-direction.getVector().getX(), -direction.getVector().getY());
                    break outer;
                }
            }
        }
    }

    public void shoot() {
        if (entity.getComponent(EffectComponent.class).hasEffect(FreezeEffect.class)) {
            return;
        }
        if (shootTimer.elapsed(Config.SHOOT_DELAY)) {
            FXGL.spawn("bullet",
                    new SpawnData(entity.getCenter().subtract(8 / 2.0, 10 / 2.0))
                            .put("direction", direction)
                            .put(Config.OWNER_TYPE, entity.getType())
                            .put(Config.BULLET_LEVEL_KEY, entity.getComponent(BulletLevelComponent.class).getValue())
            );
            shootTimer.capture();
        }
    }
}
